﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
//.NET socket libraries
using System.Net;
using System.Net.Sockets;
//for threading inclu threads safe method
using System.Threading;
using System.Threading.Tasks;
using System.Net.NetworkInformation;
using System.Diagnostics;


namespace EITSurferInfinityLib
{
    public class EthernetHelper
    {
        Ethernet MsgPortGlobal;
        string DefaultIP;
        int accessCount;
        public string DeviceIP;
        public string TestText;
        public string TestType;

        public delegate void OnConnectedHandler(object sender, EventArgsMsg e);
        public event OnConnectedHandler OnConnected;

        public delegate void OnConnectionMessageHandler(object sender, EventArgsMsg e);
        public event OnConnectionMessageHandler OnConnectionMessage;

        public delegate void OnConnectionStartingHandler(object sender, EventArgsMsg e);
        public event OnConnectionStartingHandler OnConnectionStarting;

        public delegate void OnConnectionStoppedHandler(object sender, EventArgsMsg e);
        public event OnConnectionStoppedHandler OnConnectionStopped;

        List<string> IPList;
        List<string> IPListOpenPort;
        List<Ethernet> DeviceList;


        public EthernetHelper(string myDefaultIP)
        {
            DefaultIP=myDefaultIP;
            MsgPortGlobal = new Ethernet();
            TestType = "Equal";
            IPList = new List<string>();
            IPListOpenPort = new List<string>();
            DeviceList = new List<Ethernet>();
        }

        public void AutoDetect()
        {
            IPList.Clear();
            IPListOpenPort.Clear();
            DeviceList.Clear();

            if (OnConnectionStarting != null)
                OnConnectionStarting(null, null);

            DeviceIP = "";
            string localIP = GetLocalHostIPV4();
            if (ConnectionExists())
            {
                Debug.WriteLine("Network available: starting SBC detection");
                Debug.WriteLine(localIP);
                if (localIP != "")
                {
                    accessCount = 0;
                    for (int i = 0; i < 256; i++)
                    {
                        string localAddress = ChangeLocalAddress(localIP, i.ToString());
                        accessCount++;
                        TestSubnetPing(localAddress);
                    }   
                    //Parallel.For(0, 256, i =>
                    //{
                    //    string localAddress = ChangeLocalAddress(localIP, i.ToString());
                    //    accessCount++;
                    //    TestSubnetPing(localAddress);
                    //}
                    //);

                    //TestAllConnection();
                }

            }
            else
            {
                Debug.WriteLine("No network available: trying to connect to the default SBC address :" + DefaultIP);
                TestConnectionToSBC(DefaultIP);
                TestAllConnection();
            }
        }

        public void AutoDetect2()
        {
            int NOfTentatives = 30;
            string[] iplist = new string[NOfTentatives];
            int[] iprank = new int[NOfTentatives];
            DeviceIP="";
            string localIP=GetLocalHostIPV4();
            if (ConnectionExists())
            {
                Debug.WriteLine("Network available: stating SBC detection");
                Debug.WriteLine(localIP);
                if (localIP != "")
                {
                    for (int i = 0; i < iplist.Length; i++)
                    {
                        iplist[i] = GetSubnetBroadcast(GetBroadCastMsg(localIP));
                        Thread.Sleep(50);
                    }

                    int[] count = new int[NOfTentatives];
                    for (int j = 0; j < iplist.Length; j++)
                    {
                        for (int i = 0; i < iplist.Length; i++)
                        {
                            if (iplist[j] == iplist[i])
                            {
                                count[j]++;
                            }
                        }
                    }

                    var x = count.ToList();
                    DeviceIP=iplist[x.IndexOf(count.Max())];
                    if (DeviceIP != "")
                    {
                        TestConnectionToSBC(DeviceIP);
                    }
                }
            }
            else 
            {
                Debug.WriteLine("No network available: trying to connect to the default SBC address :"+ DefaultIP);
                TestConnectionToSBC(DefaultIP);
                TestAllConnection();
            }
        }

        private bool ConnectionExists()
        {
            try
            {
                System.Net.Sockets.TcpClient clnt = new System.Net.Sockets.TcpClient("www.google.com", 80);
                clnt.Close();
                return true;
            }
            catch (System.Exception ex)
            {
                return false;
            }
        }

        private string GetLocalHostIPV4()
        {
            IPHostEntry host;
            string localIP = "";
            host = Dns.GetHostEntry(Dns.GetHostName());
            foreach (IPAddress ip in host.AddressList)
            {
                if (ip.AddressFamily == AddressFamily.InterNetwork)
                {
                    localIP = ip.ToString();
                }
            }
            return localIP;
        }
        private string ChangeLocalAddress(string localIP, string last)
        {
            //creates the broadcasting message
            string[] SplitAddress = localIP.Split('.');
            SplitAddress[SplitAddress.Length - 1] = last;
            string Broadcast = "";
            for (int i = 0; i < SplitAddress.Length - 1; i++)
            {
                Broadcast += SplitAddress[i] + ".";
            }
            Broadcast += SplitAddress[SplitAddress.Length - 1];
            Debug.WriteLine(Broadcast);
            return Broadcast;
        }
        private string GetBroadCastMsg(string localIP)
        {
            //creates the broadcasting message
            string[] SplitAddress = localIP.Split('.');
            SplitAddress[SplitAddress.Length - 1] = "255";
            string Broadcast = "";
            for (int i = 0; i < SplitAddress.Length - 1; i++)
            {
                Broadcast += SplitAddress[i] + ".";
            }
            Broadcast += SplitAddress[SplitAddress.Length - 1];
            Debug.WriteLine(Broadcast);
            return Broadcast;
        }
        private void TestSubnetPing(string address)
        {
            //start the ping procedure
            Ping pingSender = new Ping();
            pingSender.PingCompleted += new PingCompletedEventHandler(pingSender_Complete);
            AutoResetEvent resetEvent = new AutoResetEvent(false);
            PingOptions options = new PingOptions(2,true);
            options.DontFragment = true;
            // Create a buffer of 32 bytes of data to be transmitted.  
            string data = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
            byte[] buffer = Encoding.ASCII.GetBytes(data);
            int timeout = 2;
            try
            {
                pingSender.SendAsync(address, timeout, buffer, options, resetEvent);
                //PingReply pingResponse=pingSender.Send(address, timeout, buffer, options);
                //// Call the method that displays the ping results, and pass the information with it
                //if (pingResponse.Status == IPStatus.Success)
                //{
                //    IPList.Add(pingResponse.Address.ToString());
                //    Debug.WriteLine(pingResponse.Address.ToString() + " is responding");
                //    //TestConnectionToSBC(pingResponse.Address.ToString());
                //}
            }
            catch (Exception ex)
            {
                RaiseMessageEvent(ex.Message);
                Debug.Write(ex.Message);
            }
        }
        private void pingSender_Complete(object sender, PingCompletedEventArgs e)
        {
            // If the operation was canceled, display a message to the user.

            if (e.Cancelled)
            {
                Debug.WriteLine( "Ping was canceled...\r\n");
                // The main thread can resume
                ((AutoResetEvent)e.UserState).Set();
            }

            else if (e.Error != null)
            {
                Debug.WriteLine("An error occured: " + e.Error + "\r\n") ;
                // The main thread can resume
                ((AutoResetEvent)e.UserState).Set();
            }
            else
            {
                PingReply pingResponse = e.Reply;
                // Call the method that displays the ping results, and pass the information with it
                if (pingResponse.Status == IPStatus.Success)
                {
                    IPList.Add(pingResponse.Address.ToString());
                    Debug.WriteLine(pingResponse.Address.ToString() + " is responding");
                    RaiseMessageEvent(pingResponse.Address.ToString() + " is responding");
                    //TestConnectionToSBC(pingResponse.Address.ToString());
                }
                accessCount--;
            }
            ((IDisposable)sender).Dispose();
            if (accessCount == 0)
            {
                TestAllConnection();
            }
        }
        private void TestAllConnection()
        {
            //for(int i = 0; i<IPList.Count; i++)
            //{
            //    TestConnectionToSBC(IPList[i]);
            //}
            Parallel.For(0, IPList.Count, i =>
            {
                TestConnectionToSBC(IPList[i]);
            }
            );
            for (int i = 0; i < IPListOpenPort.Count; i++)
            {
                TestConnectionToSBCWithOpenPort(IPListOpenPort[i]);
            }
            if (OnConnectionStopped != null)
                OnConnectionStopped(null, null);
        }
        private string GetSubnetBroadcast(string myBroadcast)
        {
            //start the ping procedure
            Ping pingSender = new Ping();
            PingOptions options = new PingOptions();
            options.DontFragment = true;
            // Create a buffer of 32 bytes of data to be transmitted.  
            string data = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
            byte[] buffer = Encoding.ASCII.GetBytes(data);
            int timeout = 120;
            try
            {
                PingReply reply = pingSender.Send(myBroadcast, timeout, buffer, options);
                if (reply.Status == IPStatus.Success)
                {
                    Debug.WriteLine("Ping was successful.");
                    Debug.WriteLine(reply.Address);
                    return reply.Address.ToString();
                }
                else
                {
                    Debug.WriteLine("Ping failed.");
                }
            }
            catch (Exception ex)
            {
                Debug.Write(ex.Message);
                RaiseMessageEvent(ex.Message);
            }
            return "";
        }
        private void TestConnectionToSBC(string myDevieIP)
        {
            Ethernet MsgPort = new Ethernet();
            Debug.WriteLine("Begin test connection with: "+myDevieIP);
            MsgPort.SetIPAddress(myDevieIP);
            MsgPort.Port = 10000;
            try
            {
                MsgPort.Connect();
            }
            catch (Exception e)
            { }
            if (MsgPort.IsConnected())
            {
                Debug.WriteLine("start testing device: " + myDevieIP);
                RaiseMessageEvent("start testing device: " + myDevieIP);

                IPListOpenPort.Add(myDevieIP);
                MsgPort.Disconnect();
            }
            else
            {
                Debug.WriteLine("No open port on device: " + myDevieIP);
                RaiseMessageEvent("No open port on device: " + myDevieIP);
            }
        }
        private void RaiseMessageEvent(string s)
        {
            EventArgsMsg ev = new EventArgsMsg("Autodetect", s+"\r");
                //
            //lock (OnConnectionMessage)
            //    {
                    if (OnConnectionMessage != null)
                        OnConnectionMessage(null, ev);
                //}
        }
        private void TestConnectionToSBCWithOpenPort(string myDevieIP)
        {
            Debug.WriteLine("Begin test connection with: " + myDevieIP);
            MsgPortGlobal.SetIPAddress(myDevieIP);
            MsgPortGlobal.Port = 10000;
            MsgPortGlobal.OnReceived += new Ethernet.OnReceivedHandler(MsgPort_OnReceived);
            try
            {
                MsgPortGlobal.Connect();
            }
            catch (Exception e)
            { }
            if (MsgPortGlobal.IsConnected())
            {
                RaiseMessageEvent("device connected on: " + myDevieIP);
                Debug.WriteLine("device connected on: " + myDevieIP);
            }
            else
            {
                RaiseMessageEvent("device not connected on: " + myDevieIP);
                Debug.WriteLine("device not connected on: " + myDevieIP);
            }
        }
        private void MsgPort_OnReceived(Object sender, EventArgs e)
        {
            Debug.WriteLine("msg");
            char[] chars = new char[MsgPortGlobal.DataByte.Count];
            System.Text.Decoder d = System.Text.Encoding.UTF8.GetDecoder();
            int charLen = d.GetChars(MsgPortGlobal.DataByte.ToArray(), 0, MsgPortGlobal.DataByte.Count, chars, 0);
            System.String s = new System.String(chars);
            Debug.WriteLine(s);
            RaiseMessageEvent(s);
            bool DoEvent = false;
            if (TestType == "Equal")
            {
                if (TestText == s)
                {
                    DoEvent = true;
                }
            }
            else if(TestType=="Contains")
            {
                if (s.Contains(TestText))
                {
                    DoEvent = true;
                }
            }
            if (DoEvent)
            {
                EventArgsMsg ev = new EventArgsMsg("Autodetect", MsgPortGlobal.IPName.ToString());
                MsgPortGlobal.Disconnect();
                Thread.Sleep(100);
                lock (OnConnected)
                {
                    if (OnConnected != null)
                        OnConnected(null, ev);
                }
            }
        }
    }
    
}
